/*===============================================
Copyright (c): Technology Co.,Ltd. ALL rights reserved. 
                                                                 
  Create by:Lin
      Email:
       Date:
   Filename:
Description:
    Version:
Last Change:
                                                                 
=======================================*/
                                                                 
`ifndef SWITCH_SCB_SV
`define SWITCH_SCB_SV

class switch_scb extends uvm_component;
	`uvm_component_utils(switch_scb)

	int pass_packet = 0;
	int total_packet = 0;
	int max_delay_time[PORT_NUM] = '{PORT_NUM{0}}, arv_delay_time[PORT_NUM] = '{PORT_NUM{0}}, min_delay_time[PORT_NUM] = '{PORT_NUM{1000000}}, tmp_delay_time[PORT_NUM] = '{PORT_NUM{0}};
	int f_max_delay_time = 0, f_arv_delay_time = 0, f_min_delay_time = 1000000;
	real port_total_data_bytes[PORT_NUM] = '{PORT_NUM{0}};
	real port_total_time_start[PORT_NUM] = '{PORT_NUM{0}};
	real port_total_time[PORT_NUM] = '{PORT_NUM{0}};
	virtual interface test_if m_test_if;

	uvm_blocking_get_port #(mac_seq_item) exp_port[PORT_NUM];
	uvm_blocking_get_port #(mac_seq_item) act_port[PORT_NUM];
	extern function new(string name, uvm_component parent);
	extern virtual function void build_phase(uvm_phase phase);
	extern task main_phase(uvm_phase phase);
	extern task post_main_phase(uvm_phase phase);
endclass

function switch_scb::new(string name, uvm_component parent);
	super.new(name, parent);
endfunction

function void switch_scb::build_phase(uvm_phase phase);
	super.build_phase(phase);
	for (int i = 0; i < PORT_NUM; i++) begin
		exp_port[i] = new($sformatf("exp_port_%0d", i), this);
		act_port[i] = new($sformatf("act_port_%0d", i), this);
	end
	if(!uvm_config_db#(virtual test_if #(PORT_NUM))::get(this, "", "t_if", m_test_if))
		`uvm_fatal(get_type_name(), "virtual interface must be set for !!!");

endfunction

task switch_scb::main_phase(uvm_phase phase);
	mac_seq_item get_expect[PORT_NUM], get_actual[PORT_NUM], tmp_tran_exp[PORT_NUM];
	typedef mac_seq_item ex_queue[$];
	ex_queue temp_expect_queue[PORT_NUM];
	ex_queue expect_queue[PORT_NUM];
	bit [4:0] error_count[PORT_NUM] = {0,0,0,0};
	int find_exp_flag[PORT_NUM] = {0,0,0,0};


	for (int i = 0; i < PORT_NUM; i++) 
	fork
		automatic int k = i;
		while(1) begin
			exp_port[k].get(get_expect[k]);
			total_packet++;
			expect_queue[k].push_back(get_expect[k]);
		end
	join_none

	for (int j = 0; j < PORT_NUM; j++)
	fork
		automatic int k = j;
		while(1) begin
			#20;
			tmp_tran_exp[k] = new($sformatf("tmp_tran_exp_%0d", k));
			act_port[k].get(get_actual[k]);
			if (port_total_time_start[k] == 0) begin
				port_total_time_start[k] = get_actual[k].output_time;
			end
			else begin
				port_total_time[k] = $time - port_total_time_start[k];
			end
			port_total_data_bytes[k] = port_total_data_bytes[k] + get_actual[k].pload.size() +  get_actual[k].frame_pre.size() + 19;
			find_exp_flag[k] = 0;
			while (expect_queue[k].size() > 0)	begin
				tmp_tran_exp[k] = expect_queue[k].pop_front();
				tmp_tran_exp[k].port_num = k;
				if (tmp_tran_exp[k].destiny_mac_addr == get_actual[k].destiny_mac_addr && tmp_tran_exp[k].pload == get_actual[k].pload) begin
					find_exp_flag[k] = 1;
					tmp_delay_time[k] = get_actual[k].output_time - tmp_tran_exp[k].input_time;
					min_delay_time[k] = min_delay_time[k] < tmp_delay_time[k] ? min_delay_time[k] : tmp_delay_time[k];
					max_delay_time[k] = max_delay_time[k] > tmp_delay_time[k] ? max_delay_time[k] : tmp_delay_time[k];

					if (arv_delay_time[k] == 0) begin
						arv_delay_time[k] = tmp_delay_time[k];
					end
					else begin
						arv_delay_time[k] = (arv_delay_time[k] + tmp_delay_time[k]) / 2;
					end

					$display("\n\033[36m*************TIME:%8t***************", $time);

					break;
				end else begin
					temp_expect_queue[k].push_back(tmp_tran_exp[k]);
				end
			end
			while (temp_expect_queue[k].size() > 0) begin
				expect_queue[k].push_front(temp_expect_queue[k].pop_back());
			end
			if (find_exp_flag[k] == 0) begin
				$display("\n\033[36m*************TIME:%8t***************", $time);
				if (expect_queue[k].size != 0) begin
					tmp_tran_exp[k] = expect_queue[k].pop_front();
				end
				else begin
					$display("\n\033[36m********EXPECT QUEUE IS EMPTY********");	
				end
			end


			if (tmp_tran_exp[k].destiny_mac_addr != get_actual[k].destiny_mac_addr) begin
				$display("\033[36m*\033[31mPacket[%0d]: Destiny_mac_addr Compare FAIL\033[0m", tmp_tran_exp[k].pkt_id);
				error_count[k][0] = 1;
			end
			if (tmp_tran_exp[k].source_mac_addr != get_actual[k].source_mac_addr) begin
				$display("\033[36m*\033[31mPacket[%0d]: Source_mac_addr Compare FAIL\033[0m", tmp_tran_exp[k].pkt_id);
				error_count[k][1] = 1;
			end
			if (tmp_tran_exp[k].frame_type != get_actual[k].frame_type) begin
				$display("\033[36m*\033[31mPacket[%0d]: Frame_type Compare FAIL\033[0m", tmp_tran_exp[k].pkt_id);
				error_count[k][2] = 1;
			end
			if (tmp_tran_exp[k].pload != get_actual[k].pload) begin
				$display("\033[36m*\033[31mPacket[%0d]: Pload Compare FAIL\033[0m", tmp_tran_exp[k].pkt_id);
				error_count[k][3] = 1;
			end
			if (tmp_tran_exp[k].crc != get_actual[k].crc) begin
				$display("\033[36m*\033[31mPacket[%0d]: CRC32 Compare FAIL\033[0m", tmp_tran_exp[k].pkt_id);
				error_count[k][4] = 1;
			end
			if (error_count[k] == 0) begin
				$display("\033[36m*\033[32mPacket[%0d] OUT_PORT:[%0d] COMPARE PASS!!!",	tmp_tran_exp[k].pkt_id, tmp_tran_exp[k].port_num);	
				$display("\033[36m*****************************************\033[0m\n");
				pass_packet++;
			end
			else begin
				$display("\033[36m*\n*\033[31mPacket[%0d] OUT_PORT:[%0d] COMPARE FAIL!!!", tmp_tran_exp[k].pkt_id, tmp_tran_exp[k].port_num);	
				$display("\033[36m*****************************************\033[0m\n");
				$display("\033[36mThe Expect Pkt is\033[0m");
				tmp_tran_exp[k].my_printf();
				$display("\033[36mThe Actual Pkt is\033[0m");
				get_actual[k].my_printf();
				error_count[k] = 0;
				$display("\033[36mThe expect queue size is %0d\033[0m", expect_queue[k].size());
				while (expect_queue[k].size() > 0) begin
					tmp_tran_exp[k] = expect_queue[k].pop_front();
					tmp_tran_exp[k].my_printf();
				end
				`uvm_error("TESTFAIL", {"test fail", get_full_name()})

			end


		end
	join_none
endtask

task switch_scb::post_main_phase(uvm_phase phase);

	$display("\033[32m*** Expect output total packets num is %2d, pass packets num is %2d\033[36m ***", total_packet, pass_packet);
	$display("\033[36m\n***********************************************************************************************");
	$display("***\033[32m      port 0 drop num is %2d,   Delay(ns): %8d (min) %8d(max) %8d(arv)    \033[36m ***", m_test_if.drop_num[0], min_delay_time[0], max_delay_time[0], arv_delay_time[0]);
	$display("***\033[32m      port 1 drop num is %2d,   Delay(ns): %8d (min) %8d(max) %8d(arv)    \033[36m ***", m_test_if.drop_num[1], min_delay_time[1], max_delay_time[1], arv_delay_time[1]);
	$display("***\033[32m      port 2 drop num is %2d,   Delay(ns): %8d (min) %8d(max) %8d(arv)    \033[36m ***", m_test_if.drop_num[2], min_delay_time[2], max_delay_time[2], arv_delay_time[2]);
	$display("***\033[32m      port 3 drop num is %2d,   Delay(ns): %8d (min) %8d(max) %8d(arv)    \033[36m ***", m_test_if.drop_num[3], min_delay_time[3], max_delay_time[3], arv_delay_time[3]);
	$display("***********************************************************************************************\033[0m");
	for (int i = 0; i < PORT_NUM; i++) begin
		f_min_delay_time = min_delay_time[i] < f_min_delay_time ? min_delay_time[i] : f_min_delay_time;
		f_max_delay_time = max_delay_time[i] > f_max_delay_time ? max_delay_time[i] : f_max_delay_time;
		f_arv_delay_time += arv_delay_time[i];
	end
	f_arv_delay_time /= 4;
	
	$display("\033[36m\n*****************************************");
	$display("*                                       *");
	$display("***\033[32m    MIN delay time: %8d ns   \033[36m ***", f_min_delay_time);		$display("***\033[32m    MAX delay time: %8d ns   \033[36m ***", f_max_delay_time);
	$display("***\033[32m    ARV delay time: %8d ns   \033[36m ***", f_arv_delay_time);
	$display("*                                       *");
	$display("*****************************************\033[0m");
	$display("\033[36m\n*****************************************");
	$display("*                                       *");
	$display("***\033[32m    port 0 SPEED: %.2f Mbps   \033[36m ***", (port_total_data_bytes[0] * 8) / (port_total_time[0] / 1000));
	$display("***\033[32m    port 1 SPEED: %.2f Mbps   \033[36m ***", (port_total_data_bytes[1] * 8) / (port_total_time[1] / 1000));
	$display("***\033[32m    port 2 SPEED: %.2f Mbps   \033[36m ***", (port_total_data_bytes[2] * 8) / (port_total_time[2] / 1000));
	$display("***\033[32m    port 3 SPEED: %.2f Mbps   \033[36m ***", (port_total_data_bytes[3] * 8) / (port_total_time[3] / 1000));
	$display("*                                       *");
	$display("*****************************************\033[0m");
	pass_packet = pass_packet + m_test_if.drop_num[0] + m_test_if.drop_num[1]+ m_test_if.drop_num[2] + m_test_if.drop_num[3];
	if (pass_packet == total_packet) begin
		$display("\033[36m\n*****************************************");
		$display("*                                       *");
		$display("*                                       *");
		$display("***\033[32m           TEST PASS!!!          \033[36m ***");
		$display("*                                       *");
		$display("*                                       *");
		$display("*****************************************\033[0m");
	end
	else begin
		$display("\033[36m\n*****************************************");
		$display("*                                       *");
		$display("*                                       *");
		$display("**\033[31m       TEST FAIL, PASSRATE:%2d%%     \033[36m **", pass_packet * 100 /	total_packet);
		$display("*                                       *");
		$display("*                                       *");
		$display("*****************************************\033[0m");
	end




	
endtask;


`endif
